package parser;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import stackexchange.Badge;
import stackexchange.User;
import stackexchange.UserGroup;
import stackexchange.posts.Answer;
import stackexchange.posts.Comment;
import stackexchange.posts.Question;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Vector;

import static stackexchange.Constants.setCalendar;

public class XmlParser {
	private Calendar CURRENT_DATE = new GregorianCalendar();
	private String locOfFiles;

	private Vector<Question> allQuestions = new Vector<Question>();
	private Vector<Answer> allAnswers = new Vector<Answer>();
	private Vector<User> allUsers = new Vector<User>();
	private UserGroup allUsersGroup;

	// private Vector<Tag> allTags = new Vector<Tag>();

	public XmlParser(String locOfFiles) {
		setCalendar();
		this.locOfFiles = locOfFiles;
		initializeUsers();
		initializePosts();
		analyzeQuestionAndAnswers();
		bindPostsAndUsers();
		setReputation();
		allUsersGroup = new UserGroup(allUsers);
		addComments();
		addBadges();
		addPostHistory();
	}

	public XmlParser() {

	}

	private boolean ownerAvailable(int id) {
		for (int i = 0; i < allUsers.size(); i++) {
			if (allUsers.get(i).getId() == id) {
				return true;
			}
		}
		return false;
	}

	private void initializeUsers() {

		try {
			FileInputStream file = new FileInputStream(new File(locOfFiles
					+ "/users.xml"));
			DocumentBuilderFactory builderFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = builderFactory.newDocumentBuilder();
			Document xmlDocument = builder.parse(file);
			XPath xPath = XPathFactory.newInstance().newXPath();

			String expression = "users/row";
			NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(
					xmlDocument, XPathConstants.NODESET);
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				NamedNodeMap nodeAttrs = node.getAttributes();

				int accountId = 3;// TODO
				// Integer.parseInt(nodeAttrs.getNamedItem(
				// "AccountId").getNodeValue());
				int id = Integer.parseInt(nodeAttrs.getNamedItem("Id")
						.getNodeValue());
				String creationDate = nodeAttrs.getNamedItem("CreationDate")
						.getNodeValue();
				String lastAccessDate = nodeAttrs
						.getNamedItem("LastAccessDate").getNodeValue();
				String name = nodeAttrs.getNamedItem("DisplayName")
						.getNodeValue();
				int downVotes = Integer.parseInt(nodeAttrs.getNamedItem(
						"DownVotes").getNodeValue());
				int reputation = Integer.parseInt(nodeAttrs.getNamedItem(
						"Reputation").getNodeValue());
				int upVotes = Integer.parseInt(nodeAttrs
						.getNamedItem("UpVotes").getNodeValue());
				int views = Integer.parseInt(nodeAttrs.getNamedItem("Views")
						.getNodeValue());

				User user = new User(id, name, accountId, reputation,
						setDate(creationDate), setDate(lastAccessDate), views,
						upVotes, downVotes);

				allUsers.add(user);
				//
				// (int id, String name, String nthUser, int reputation, String
				// creationDate,
				// String lastAccessDate, int profileViews, int upVotes, int
				// downVotes)
				//
				// System.out.print(nodeAttrs.getNamedItem("LastAccessDate")
				// .getNodeValue());
				// for (int j = 0; null != nodeAttrs && j <
				// nodeAttrs.getLength(); j++) {
				// System.out.print(nodeAttrs.item(j).getNodeName() + " - "
				// + nodeAttrs.item(j).getNodeValue());
				// System.out.println();
				// }
				// System.out.println("***");
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
	}

	private String parseIfExists(NamedNodeMap nodeAttrs, String attr) {
		String result = "";
		try {
			result = nodeAttrs.getNamedItem(attr).getNodeValue();
		} catch (Exception e) {
			result = "0";
		}
		return result;
	}

	private void initializePosts() {
		try {
			FileInputStream file = new FileInputStream(new File(locOfFiles
					+ "/posts.xml"));

			DocumentBuilderFactory builderFactory = DocumentBuilderFactory
					.newInstance();

			DocumentBuilder builder = builderFactory.newDocumentBuilder();

			Document xmlDocument = builder.parse(file);

			XPath xPath = XPathFactory.newInstance().newXPath();

			String expression = "posts/row";
			NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(
					xmlDocument, XPathConstants.NODESET);

			for (int i = 0; i < nodeList.getLength(); i++) {
				// System.out.println(nodeList.item(i).getFirstChild().getNodeValue());
				Node node = nodeList.item(i);
				NamedNodeMap nodeAttrs = node.getAttributes();

				// Mandatory
				int postId = Integer.parseInt(nodeAttrs.getNamedItem("Id")
						.getNodeValue());
				String creationDate = nodeAttrs.getNamedItem("CreationDate")
						.getNodeValue();
				String lastActivityDate = nodeAttrs.getNamedItem(
						"LastActivityDate").getNodeValue();
				int score = Integer.parseInt(nodeAttrs.getNamedItem("Score")
						.getNodeValue());
				String body = nodeAttrs.getNamedItem("Body").getNodeValue();

				// Optional
				int nComments = Integer.parseInt(parseIfExists(nodeAttrs,
						"CommentCount"));
				String lastEditDate = parseIfExists(nodeAttrs, "LastEditDate");

				// Check whether the user is deleted or not
				int ownerUserId = Integer.parseInt(parseIfExists(nodeAttrs,
						"OwnerUserId"));
				if (ownerUserId == 0 || ownerUserId == -1 || ownerUserId == 1
						|| !ownerAvailable(ownerUserId)) {
					// user is deleted
					// thus, out of consideration
				}
				// If the post is a question
				else if (nodeAttrs.getNamedItem("PostTypeId").getNodeValue()
						.equalsIgnoreCase("1")) {

					int acceptedAnswerId = Integer.parseInt(parseIfExists(
							nodeAttrs, "AcceptedAnswerId"));
					int nAnswers = Integer.parseInt(parseIfExists(nodeAttrs,
							"AnswerCount"));
					int nViews = Integer.parseInt(nodeAttrs.getNamedItem(
							"ViewCount").getNodeValue());
					String title = nodeAttrs.getNamedItem("Title")
							.getNodeValue();
					Vector<String> tags = createTags(nodeAttrs.getNamedItem(
							"Tags").getNodeValue());

					Question question = new Question(postId,
							setDate(creationDate), score, body, ownerUserId,
							setDate(lastEditDate), setDate(lastActivityDate),
							nComments, acceptedAnswerId, nAnswers, nViews,
							title, tags);
					allQuestions.add(question);

				} else if (nodeAttrs.getNamedItem("PostTypeId").getNodeValue()
						.equalsIgnoreCase("2")) {// If the post is an answer

					int parentId = Integer.parseInt(nodeAttrs.getNamedItem(
							"ParentId").getNodeValue());

					Answer answer = new Answer(postId, setDate(creationDate),
							score, body, ownerUserId, setDate(lastEditDate),
							setDate(lastActivityDate), nComments, parentId);

					allAnswers.add(answer);
				}

			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
	}

	private void addComments() {
		Vector<Comment> allComments = new Vector<Comment>();
		try {
			FileInputStream file = new FileInputStream(new File(locOfFiles
					+ "/Comments.xml"));
			DocumentBuilderFactory builderFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = builderFactory.newDocumentBuilder();
			Document xmlDocument = builder.parse(file);
			XPath xPath = XPathFactory.newInstance().newXPath();
			String expression = "comments/row";
			NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(
					xmlDocument, XPathConstants.NODESET);

			for (int i = 0; i < nodeList.getLength(); i++) {
				// System.out.println(nodeList.item(i).getFirstChild().getNodeValue());
				Node node = nodeList.item(i);
				NamedNodeMap nodeAttrs = node.getAttributes();

				// Mandatory
				int userId = Integer
						.parseInt(parseIfExists(nodeAttrs, "UserId"));
				String creationDate = nodeAttrs.getNamedItem("CreationDate")
						.getNodeValue();
				int score = Integer.parseInt(parseIfExists(nodeAttrs, "Score"));
				if (userId != 0 && userId != -1 && userId != 1
						&& ownerAvailable(userId)) {
					allComments.add(new Comment(userId, setDate(creationDate),
							score));
				}
			}
			for (int i = 0; i < allComments.size(); i++) {
				allUsersGroup.getUser(allComments.get(i).getUserId())
						.addComment(allComments.get(i));
			}
			allUsers = allUsersGroup.getUsers();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
	}

	private void addBadges() {
		Vector<Badge> allBadges = new Vector<Badge>();
		try {
			FileInputStream file = new FileInputStream(new File(locOfFiles
					+ "/Badges.xml"));
			DocumentBuilderFactory builderFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = builderFactory.newDocumentBuilder();
			Document xmlDocument = builder.parse(file);
			XPath xPath = XPathFactory.newInstance().newXPath();
			String expression = "badges/row";
			NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(
					xmlDocument, XPathConstants.NODESET);

			for (int i = 0; i < nodeList.getLength(); i++) {
				// System.out.println(nodeList.item(i).getFirstChild().getNodeValue());
				Node node = nodeList.item(i);
				NamedNodeMap nodeAttrs = node.getAttributes();

				// Mandatory
				int userId = Integer
						.parseInt(parseIfExists(nodeAttrs, "UserId"));
				String creationDate = nodeAttrs.getNamedItem("Date")
						.getNodeValue();
				String name = nodeAttrs.getNamedItem("Name").getNodeValue();
				if (userId != 0 && userId != -1 && userId != 1
						&& ownerAvailable(userId)) {
					allBadges
							.add(new Badge(userId, setDate(creationDate), name));
				}
			}
			for (int i = 0; i < allBadges.size(); i++) {
				allUsersGroup.getUser(allBadges.get(i).getUserId()).addBadge(
						allBadges.get(i));
			}
			allUsers = allUsersGroup.getUsers();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
	}

	private void addPostHistory() {
		try {
			FileInputStream file = new FileInputStream(new File(locOfFiles
					+ "/PostHistory.xml"));
			DocumentBuilderFactory builderFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = builderFactory.newDocumentBuilder();
			Document xmlDocument = builder.parse(file);
			XPath xPath = XPathFactory.newInstance().newXPath();
			String expression = "posthistory/row";
			NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(
					xmlDocument, XPathConstants.NODESET);

			for (int i = 0; i < nodeList.getLength(); i++) {
				// System.out.println(nodeList.item(i).getFirstChild().getNodeValue());
				Node node = nodeList.item(i);
				NamedNodeMap nodeAttrs = node.getAttributes();

				// Mandatory
				int userId = Integer
						.parseInt(parseIfExists(nodeAttrs, "UserId"));
				int postId = Integer.parseInt(nodeAttrs.getNamedItem("PostId")
						.getNodeValue());
				if (userId != 0 && userId != -1 && userId != 1
						&& ownerAvailable(userId)) {
					boolean found = false;
					// for(int k = 0; !found && k < allQuestions.size(); k++){
					// onnodig?
					// if(allQuestions.get(i).getPostId() == postId){
					// allQuestions.get(i).addEditorUserId(userId);
					// found = true;
					// }
					// }
					allUsersGroup.getUser(userId).increaseNofEdits();
					for (int k = 0; k < allAnswers.size(); k++) {
						if (allAnswers.get(k).getPostId() == postId) {
							allAnswers.get(k).addEditorUserId(userId);
						}
					}
				}
			}
			allUsers = allUsersGroup.getUsers();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
	}

	private void setReputation() {
		for (int i = 0; i < allUsers.size(); i++) {
			allUsers.get(i).setReputation();
		}
	}

	private void analyzeQuestionAndAnswers() {
		// set choosen answers
		Vector<Integer> acceptedAnswerIds = new Vector<Integer>();
		for (int i = 0; i < allQuestions.size(); i++) {
			if (allQuestions.get(i).isAnswerAccepted()) {
				acceptedAnswerIds
						.add(allQuestions.get(i).getAcceptedAnswerId());
			}
		}
		for (int i = 0; i < allAnswers.size(); i++) {
			for (int j = 0; j < acceptedAnswerIds.size(); j++) {
				if (allAnswers.get(i).getPostId() == acceptedAnswerIds.get(j)) {
					allAnswers.get(i).setAnAcceptedAnswer(true);
				}
			}
			// if (acceptedAnswerIds.contains(allAnswers.get(i).getPostId())) {
			// allAnswers.get(i).setAnAcceptedAnswer(true);
			// }
		}

		// set most popular answers
		for (int i = 0; i < allQuestions.size(); i++) {
			if (allQuestions.get(i).isAnswered()) {
				int questionId = allQuestions.get(i).getPostId();
				int index = -1;
				int bestScore = -1000;
				for (int j = 0; j < allAnswers.size(); j++) {
					Answer answer = allAnswers.get(j);
					if (answer.getParentId() == questionId
							&& answer.getScore() > bestScore) {
						index = j;
						bestScore = answer.getScore();
					}
				}
				if (index != -1)
					allAnswers.get(index).setAnMostRatedAnswer(true);
			}
		}

	}

	private void bindPostsAndUsers() {
		for (int i = 0; i < allQuestions.size(); i++) {
			for (int j = 0; j < allUsers.size(); j++) {
				if (allQuestions.get(i).getOwnedUserId() == allUsers.get(j)
						.getId()) {
					// System.out.print("gelioyala");
					allUsers.get(j).addQuestion(allQuestions.get(i));
				}
			}
		}

		for (int i = 0; i < allAnswers.size(); i++) {
			for (int j = 0; j < allUsers.size(); j++) {
				if (allAnswers.get(i).getOwnedUserId() == allUsers.get(j)
						.getId()) {
					allUsers.get(j).addAnswer(allAnswers.get(i));
				}
			}
		}

	}

	private Vector<String> createTags(String nodeValue) {
		// TODO Auto-generated method stub
		String[] tags = nodeValue.split("<");
		Vector<String> result = new Vector<String>();
		for (int i = 0; i < tags.length; i++) {
			if (tags[i].length() > 2) {
				result.add(tags[i].substring(0, tags[i].length() - 1));
			}
		}
		return result;
	}

	public Calendar setDate(String dateInString) {
		if (dateInString.equalsIgnoreCase("0")) {
			return new GregorianCalendar();
		} else {
			Calendar c = new GregorianCalendar();
			String[] dt = dateInString.split("T");
			String[] date = dt[0].split("-");
			String[] time = dt[1].split(":");
			String second = time[2].substring(0, 1);
			c.set(Calendar.YEAR, Integer.valueOf(date[0]));
			c.set(Calendar.MONTH, Integer.valueOf(date[1]) - 1);
			c.set(Calendar.DAY_OF_MONTH, Integer.valueOf(date[2]));
			c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time[0]));
			c.set(Calendar.MINUTE, Integer.valueOf(time[1]));
			c.set(Calendar.SECOND, Integer.valueOf(second));
			return c;
		}
	}

	public String getLocOfFiles() {
		return locOfFiles;
	}

	public Vector<Question> getAllQuestions() {
		return allQuestions;
	}

	public Vector<Answer> getAllAnswers() {
		return allAnswers;
	}

	public Calendar getCurrentDate() {
		return CURRENT_DATE;
	}

	public Vector<User> getAllUsers() {
		return allUsers;
	}
}